package xin.marcher.module.trace.repository;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import xin.marcher.module.common.core.PageResult;
import xin.marcher.module.common.domain.TraceData;
import xin.marcher.module.trace.domain.dto.TraceDataDTO;
import xin.marcher.module.trace.domain.request.QueryTraceDataPageRequest;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 */
@Repository
public class TraceDataRepository {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 批量保存商品溯源数据
     */
    public void saveTraceDataList(List<TraceData> traceDataList) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (TraceData traceData : traceDataList) {
            // 1. 因为我们是日志类的数据可以直接使用es的动态mapping
            // 2. 按照月份归档存储
            String indexName = getIndexName(traceData.getTableName());
            // 3. 避免文档中的嵌套字段
            Map<String, Object> jsonMap = getJsonMap(traceData);
            IndexRequest indexRequest = new IndexRequest(indexName).source(jsonMap);
            bulkRequest.add(indexRequest);
        }
        restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }

    private Map<String, Object> getJsonMap(TraceData traceData) {
        Map<String, Object> jsonMap = new LinkedHashMap<>();
        traceData.getOldData().forEach((k, v) -> jsonMap.put("old_" + k, v));
        traceData.getNewData().forEach((k, v) -> jsonMap.put("new_" + k, v));
        return jsonMap;
    }

    private String getIndexName(String tableName) {
        String month = DateFormatUtils.format(new Date(), "_yyyy_MM");
        return tableName + "_trace_index" + month;
    }

    /**
     * 查询商品溯源数据
     */
    public PageResult<TraceDataDTO> listTraceDataPage(QueryTraceDataPageRequest request) throws IOException {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        Map<String, Object> queryParams = request.getQueryParams();
        if (MapUtils.isNotEmpty(queryParams)) {
            queryParams.forEach((k, v) -> searchSourceBuilder.query(QueryBuilders.termQuery("new_" + k, v)));
        }

        int from = (request.getPageNum() - 1) * request.getPageSize();
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(request.getPageSize());

        SearchRequest searchRequest = new SearchRequest(request.getIndexName() + "*");
        searchRequest.source(searchSourceBuilder);


        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        PageResult<TraceDataDTO> pageResult = new PageResult<>();
        List<TraceDataDTO> pageContent = new ArrayList<>();
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();

            // 取出所有的字段
            Set<String> fieldNames = sourceAsMap.keySet().stream()
                    .map(e -> e.replace("new_", "").replace("old_", ""))
                    .collect(Collectors.toSet());
            List<TraceDataDTO.FieldInfo> list = new ArrayList<>();
            for (String fieldName : fieldNames) {
                Object fieldValue = sourceAsMap.get("new_" + fieldName);
                Object oldValue = sourceAsMap.get("old_" + fieldName);
                boolean changed = !Objects.equals(fieldValue, oldValue);
                list.add(new TraceDataDTO.FieldInfo(fieldName, fieldValue, oldValue, changed));
            }

            pageContent.add(new TraceDataDTO(list));
        }

        pageResult.setContent(pageContent);
        pageResult.setTotalElements(searchResponse.getHits().getTotalHits().value);
        pageResult.setSize(request.getPageSize());
        pageResult.setNumber(request.getPageNum());
        return pageResult;
    }
}
